/*
 * @(#)SubmissionSetTransformer.java
 * Date 2013-04-10
 * Version 1.0
 * SubmissionSetAuthor Jim Horner
 * Copyright (c)2012
 */


package us.hornerscorners.lamppost.registry.transformer.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.enterprise.context.ApplicationScoped;

import javax.inject.Inject;

import oasis.names.tc.ebxml_regrep.xsd.rim._3.ClassificationType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.ExternalIdentifierType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.RegistryPackageType;
import oasis.names.tc.ebxml_regrep.xsd.rim._3.SlotType1;

import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import us.hornerscorners.lamppost.exception.ConversionException;
import us.hornerscorners.lamppost.exception.ServiceActionError;
import us.hornerscorners.lamppost.exception.ServiceActionException;
import us.hornerscorners.lamppost.exception.ValidationException;

import us.hornerscorners.lamppost.model.document.SubmissionSet;

import us.hornerscorners.lamppost.registry.constant.AvailabilityStatus;
import us.hornerscorners.lamppost.registry.constant.Metadata;
import us.hornerscorners.lamppost.registry.constant.SlotName;
import us.hornerscorners.lamppost.registry.constant.XDSDataType;

import us.hornerscorners.lamppost.registry.model.DataTypeValidator;
import us.hornerscorners.lamppost.registry.model.MetadataValidator;

import us.hornerscorners.lamppost.registry.utils.XDSUtils;

import us.hornerscorners.lamppost.transformer.AbstractTransformer;

import static us.hornerscorners.lamppost.registry.utils.XDSUtils.parseFirstValue;


/**
 *
 * @author jim
 */
@ApplicationScoped
public class SubmissionSetTransformer
        extends AbstractTransformer<RegistryPackageType, SubmissionSet> {

    /** Field description */
    private static final Logger logger =
        LoggerFactory.getLogger(SubmissionSetTransformer.class);

    /** Field description */
    @Inject
    private AttributeTransformer attributeTransformer;

    /** Field description */
    @Inject
    private AuthorTransformer authorTransformer;

    /** Field description */
    @Inject
    private ClassificationNodeTransformer classificationNodeTransformer;

    /** Field description */
    @Inject
    private CodedElementTransformer codedElementTransformer;

    /** Field description */
    @Inject
    private DataTypeValidator dataValidator;

    /** Field description */
    @Inject
    private LocalizedStringTransformer localizedStringTransformer;

    /** Field description */
    @Inject
    private MetadataValidator metaValidator;

    /** Field description */
    @Inject
    private PatientIdentifierTransformer patientIdentifierTransformer;

    /** Field description */
    @Inject
    private UniqueIdentifierTransformer uniqueIdentifierTransformer;

    /**
     * Method description
     *
     *
     * @param regpack
     *
     * @return
     *
     * @throws ConversionException
     * @throws ValidationException
     */
    @Override
    public SubmissionSet transform(RegistryPackageType regpack)
            throws ConversionException, ValidationException {

        SubmissionSet result = new SubmissionSet();

        List<ServiceActionError> errors = new ArrayList<>();

        // attributes
        result.setAvailabilityStatus(
            this.codedElementTransformer.transform(AvailabilityStatus.Submitted));

        result.setEntryURN(XDSUtils.assignEntryUUID(regpack.getId()));

        // versioning
        result.setEntryLogicalURN(StringUtils.defaultIfBlank(regpack.getLid(),
                result.getEntryURN()));
        result.setEntryVersion(
            StringUtils.defaultIfBlank(
                XDSUtils.parseVersionName(regpack.getVersionInfo()), "1"));

        // slots
        for (SlotType1 slot : regpack.getSlot()) {

            boolean validSlot = false;
            String strSlotName = slot.getName();
            SlotName slotName = SlotName.toSlotName(strSlotName);

            if (slotName != null) {

                switch (slotName) {

                    case IntendedRecipient :
                        result.getIntendedRecipients().addAll(XDSUtils.parseValues(slot));
                        validSlot = true;

                        break;

                    case SubmissionTime :
                        String value = parseFirstValue(slot);

                        errors.addAll(this.dataValidator.validate(slotName,
                                XDSDataType.dtm, value));
                        result.setSubmissionTime(XDSUtils.parseDTM(value));
                        validSlot = true;

                        break;
                }

            }

            if (validSlot == false) {

                try {

                    result.getExtraAttributes().add(
                        this.attributeTransformer.transform(slot));

                } catch (ServiceActionException e) {

                    errors.addAll(Arrays.asList(e.getErrors()));
                }
            }

        }

        // names and description
        result.getDisplayNames().addAll(
            this.localizedStringTransformer.transform(regpack.getName()));
        result.getDescriptions().addAll(
            this.localizedStringTransformer.transform(regpack.getDescription()));

        // classification
        for (ClassificationType ctype : regpack.getClassification()) {

            Metadata type = Metadata.toMetadata(ctype.getClassificationScheme());

            if (type == null) {

                type = Metadata.toMetadata(ctype.getClassificationNode());
            }

            if (type != null) {

                switch (type) {

                    case SubmissionSetClassificationNode :
                        result.setClassificationNode(
                            this.classificationNodeTransformer.transform(ctype));

                        break;

                    case SubmissionSetAuthor :
                        result.getAuthors().add(this.authorTransformer.transform(ctype));

                        break;

                    case SubmissionSetContentTypeCode :
                        result.setContentTypeCode(
                            this.codedElementTransformer.transform(ctype));

                        break;
                }
            }
        }

        // externalIdentifiers
        for (ExternalIdentifierType idtype : regpack.getExternalIdentifier()) {

            Metadata schemeType = Metadata.toMetadata(idtype.getIdentificationScheme());

            if (schemeType != null) {

                String value = idtype.getValue();

                switch (schemeType) {

                    case SubmissionSetPatientId :
                        errors.addAll(this.dataValidator.validate(schemeType,
                                XDSDataType.cx, value));
                        result.setPatientId(
                            this.patientIdentifierTransformer.transform(idtype));

                        break;

                    case SubmissionSetSourceId :
                        errors.addAll(this.dataValidator.validate(schemeType,
                                XDSDataType.oid, value));
                        result.setSourceId(
                            this.uniqueIdentifierTransformer.transform(idtype));

                        break;

                    case SubmissionSetUniqueId :
                        errors.addAll(this.dataValidator.validate(schemeType,
                                XDSDataType.oid, value));
                        result.setUniqueId(
                            this.uniqueIdentifierTransformer.transform(idtype));

                        break;
                }
            }
        }

        errors.addAll(validate(result));

        if (errors.isEmpty() == false) {

            throw new ValidationException(getErrorLocation(), errors);
        }

        return result;
    }

    /**
     * Method description
     *
     *
     * @param regpack
     * @param ctype
     *
     * @return
     *
     * @throws ConversionException
     * @throws ValidationException
     */
    public SubmissionSet transform(RegistryPackageType regpack, ClassificationType ctype)
            throws ConversionException, ValidationException {

        SubmissionSet result = transform(regpack);

        result.setClassificationNode(this.classificationNodeTransformer.transform(ctype));

        return result;
    }

    /**
     * Method description
     *
     *
     * @param subSet
     *
     * @return
     */
    private Collection<ServiceActionError> validate(SubmissionSet subSet) {

        List<ServiceActionError> errors = new ArrayList<>();

        errors.addAll(this.metaValidator.validate(Metadata.SubmissionSetContentTypeCode,
                subSet.getContentTypeCode(), true));

        errors.addAll(this.metaValidator.validate(Metadata.SubmissionSetPatientId,
                subSet.getPatientId()));

        errors.addAll(this.metaValidator.validate(Metadata.SubmissionSetSourceId,
                subSet.getSourceId()));

        errors.addAll(this.metaValidator.validate(SlotName.SubmissionTime,
                subSet.getSubmissionTime()));

        errors.addAll(this.metaValidator.validate(Metadata.SubmissionSetUniqueId,
                subSet.getUniqueId()));

        return errors;
    }
}
